package cbh.tool.inlet;

import cbh.tool.ToolBaseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by hx-pc on 16-6-14.
 */
public class AnalyzeXLSFileImpl implements AnalyzeXLSFile {

    private Workbook workbook;

    private Logger LOGGER = LoggerFactory.getLogger(AnalyzeXLSFile.class);

    /*public <T> List<T> analyzeWithSAX(File file, Class<T> valueType) throws IOException, SAXException, OpenXML4JException {
        OPCPackage pkg = OPCPackage.open(file);
        XSSFReader r = new XSSFReader(pkg);
        InputStream sheet2 = r.getSheet("rId1");
        InputStreamReader read = new InputStreamReader(sheet2);
        BufferedReader bufferedReader = new BufferedReader(read);
        String lineString = bufferedReader.readLine();
        while (lineString!=null) {
            System.out.println(lineString);
            lineString = bufferedReader.readLine();
        }
        sheet2.close();
        return null;
    }*/

    @Override
    public <T> List<T> analyze(File file, Class<T> valueType) throws IOException, ToolBaseException {
        return analyzeWithoutRowAndColumn(file, valueType, -1, -1);
    }

    @Override
    public <T> List<T> analyzeWithoutRowAndColumn(File file, Class<T> valueType, int rowNum, int columnNum) throws IOException, ToolBaseException {
        if (file == null) {
            throw new ToolBaseException("文件不能为空");
        }
        FileInputStream fis = new FileInputStream(file);
        if (file.getName().toLowerCase().endsWith("xls")) {
            workbook = new HSSFWorkbook(fis);
        } else if (file.getName().toLowerCase().endsWith("xlsx")) {
            workbook = new XSSFWorkbook(fis);
        } else {
            fis.close();
            throw new ToolBaseException("传入文件格式错误");
        }
        List<T> list = analyzeWorkbook(valueType, rowNum, columnNum, false);
        fis.close();
        workbook.close();
        return list;
    }

    @Override
    public <T> List<T> analyzeWithoutRowAndColumnRotation(File file, Class<T> valueType, int rowNum, int columnNum) throws IOException, ToolBaseException {
        if (file == null) {
            throw new ToolBaseException("文件不能为空");
        }
        FileInputStream fis = new FileInputStream(file);
        if (file.getName().toLowerCase().endsWith("xls")) {
            workbook = new HSSFWorkbook(fis);
        } else if (file.getName().toLowerCase().endsWith("xlsx")) {
            workbook = new XSSFWorkbook(fis);
        } else {
            fis.close();
            throw new ToolBaseException("传入文件格式错误");
        }
        List<T> list = analyzeWorkbook(valueType, rowNum, columnNum, true);
        fis.close();
        workbook.close();
        return list;
    }

    @Override
    public <T> List<T> analyzeWithoutRowAndColumn(String fileName, InputStream inputStream, Class<T> valueType, int rowNum, int columnNum) throws IOException, ToolBaseException {
        if (inputStream == null) {
            throw new ToolBaseException("inputStream不能为空");
        }
        if (fileName.toLowerCase().endsWith("xls")) {
            workbook = new HSSFWorkbook(inputStream);
        } else if (fileName.toLowerCase().endsWith("xlsx")) {
            workbook = new XSSFWorkbook(inputStream);
        } else {
            inputStream.close();
            throw new ToolBaseException("传入文件格式错误");
        }
        List<T> list = analyzeWorkbook(valueType, rowNum, columnNum, false);
        checkRule(list);
        inputStream.close();
        workbook.close();
        return list;
    }

    private <T> void checkRule(List<T> list) {
        for (int i = 0; i < list.size(); i++) {

        }
    }

    private <T> List<T> analyzeWorkbook(Class<T> valueType, int rowNum, int columnNum, boolean isRotation) throws IOException, ToolBaseException {
        Sheet sheet = workbook.getSheetAt(0);
        ObjectMapper objectMapper = new ObjectMapper();
        List<List<Object>> resultList = new CopyOnWriteArrayList<>();
        for (int n = sheet.getFirstRowNum(); n <= sheet.getLastRowNum(); n++) {
            Row row = sheet.getRow(n);
            List<Object> list = new CopyOnWriteArrayList<>();
            if (row != null && n > rowNum) {
                for (int m = 0; m <= sheet.getRow(0).getLastCellNum(); m++) {
                    Cell cell = row.getCell(m);
                    if (m > columnNum) {
                        if (cell != null && getValueFromCell(cell) != null) {
                            list.add(getValueFromCell(cell));
                        } else {
                            list.add("null");
                        }
                    }
                }
                resultList.add(list);
            }
        }
        if (isRotation) {
            resultList = toReverseList(resultList);
        }
        return buildObjectList(objectMapper, resultList, valueType, rowNum > 0 ? rowNum + 1 : 0);
    }

    private List<List<Object>> toReverseList(List<List<Object>> resultList) {
        List<List<Object>> returnList = new CopyOnWriteArrayList<>();
        List<Object> returnChangeList = new CopyOnWriteArrayList<>();
        for (int y = 0; y < resultList.get(0).size(); y++) {
            for (int i = 0 ; i < resultList.size(); i++) {
                returnChangeList.add(resultList.get(i).get(y));
            }
            returnChangeList = new CopyOnWriteArrayList<>();
        }
        returnList.add(returnChangeList);
        return returnList;
    }

    private <T> List<T> buildObjectList(ObjectMapper objectMapper, List<List<Object>> resultList, Class<T> valueType, Integer compensate) throws ToolBaseException {
        List<T> returnList = new CopyOnWriteArrayList<>();
        for (int n = 0; n < resultList.size(); n++) {
            String jsonObjectStr;
            try {
                jsonObjectStr = String.format(getFieldString(valueType), resultList.get(n).toArray());
            } catch (Exception e) {
                throw new ToolBaseException("excel第" + (n + compensate) + "行内容格式不匹配", e, 500);
            }
            jsonObjectStr = jsonObjectStr.replace("\"null\"", "null").replace("\n", "");
            LOGGER.info("生成的json字符串为 {}", jsonObjectStr);
            try {
                returnList.add(objectMapper.readValue(jsonObjectStr.getBytes(), valueType));
            } catch (JsonMappingException e) {
                throw new ToolBaseException("excel第" + (n + compensate) + "行内容格式不匹配", e, 500);
            } catch (Exception e) {
                throw new ToolBaseException("excel第" + (n + compensate) + "发生错误", e, 500);
            }
        }
        return returnList;
    }

    private <T> String getFieldString(Class<T> valueType) {
        StringBuilder sb = new StringBuilder("{");
        Field[] field = valueType.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            if (i != field.length - 1) {
                sb.append("\"" + field[i].getName() + "\":%s,");
            } else {
                sb.append("\"" + field[i].getName() + "\":%s");
            }
        }
        sb.append("}");
        return sb.toString();
    }

    private Object getValueFromCell(Cell cell) {
        switch (cell.getCellType()) {
            case Cell.CELL_TYPE_STRING:
                return "\"" + cell.getStringCellValue().replace("\"", "'") + "\"";
            case Cell.CELL_TYPE_NUMERIC:
                try {
                    return Long.valueOf(fmt(cell.getNumericCellValue()));
                } catch (Exception e) {
                    return Double.valueOf(fmt(cell.getNumericCellValue()));
                }
            case Cell.CELL_TYPE_BLANK:
                return null;
            default:
                return null;
        }
    }

    private String fmt(double d) {
        if (d == (long) d) {
            return String.format("%d", (long) d);
        } else
            return String.format("%s", d);
    }
}
